#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
__thread_local volatile unsigned long get_reply,put_reply;

//_thread_local_fix double A_slave[2][BLKX+2][BLKY+2][BLKZ+2];
__thread_local double A_lhs[BLKX][BLKY][BLKZ];
__thread_local double A_rhs[BLKX+2][BLKY+2][BLKZ+2];
//Record the logical information of this time executed task
__thread_local int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
//Record the logical information of the last time executed task.
__thread_local int last_iter,last_starti,last_startj,last_startk;

extern double A[2][SIZEX][SIZEY][SIZEZ];

#define alpha_d 0.0876
#define beta_d  0.0765
__thread_local int core_id=0;
#ifdef _PROFILING
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start1,dma_end1, dma_start2,dma_end2,dma_start3,dma_end3;
__thread_local volatile unsigned long s,e;
__thread_local volatile unsigned long trans_s1,trans_e1,trans_s2,trans_e2,trans_s3,trans_e3;
extern int hit[64];
extern unsigned long slave_comp_count[64];
extern unsigned long slave_trans_count[64];
//extern unsigned long slave_dma_hit_count[64];
extern unsigned long slave_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif
void stencil_core(targs* args)                                      
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
   long  array_size; 
   int tsize, bsize, stride,len;
   unsigned long ic1,ic2;
   int comp_i,comp_j,comp_k;
   int ii,jj,kk;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   s_startk = 1;
   s_endk = args->k_N-1;
//if reuse fetch
   if((s_iter-1) == last_iter && s_starti == last_starti && s_startj == last_startj && s_startk == last_startk)
   {
     core_id = athread_get_id(-1);
//     hit[core_id]++;
#ifdef _PROFILING
     dma_start1=rtc_();
     trans_s1=rtc_();
#endif
    //1.core data from lhs to rhs
     for(i = 0; i < BLKX; i++)
       for(j = 0; j < BLKY; j++)
        for(k = 0; k < BLKZ; k++)
            A_rhs[i+1][j+1][k+1] = A_lhs[i][j][k]; 
#ifdef _PROFILING
     trans_e1=rtc_();
#endif
    //2.update up and down shade data (mmem->rhs)
     get_reply = 0;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj][s_startk-1]
                 , &A_rhs[0][1][0]
                 , (BLKY)*(BLKZ+2)*8, (void*)&get_reply
                 , 0, 0, 0);
     athread_get(PE_MODE,&A[src][s_endi][s_startj][s_startk-1]
                 , &A_rhs[BLKX+1][1][0]
                 , (BLKY)*(BLKZ+2)*8,(void*)&get_reply
                 , 0, 0, 0);
     //2. update left and right shade data (mmem->lhs->rhs)
     //2.1 update left  data (mmem->lhs)
      stride = ((SIZEY-1)*SIZEZ+(SIZEZ-BLKZ))*8;
      len = BLKX*BLKZ*8;
      bsize = BLKZ*8;
      athread_get(PE_MODE,&A[src][s_starti][s_startj-1][s_startk]
                   , &A_lhs[0][0][0]
                   , len, (void*)&get_reply
                   , 0, stride, bsize);
      while(get_reply!=3);
#ifdef _PROFILING
     trans_s2=rtc_();
#endif
      //2.2 update left  data(lhs->rhs)
      for(i = 0; i < BLKX; i++)
        for(k = 0; k < BLKZ; k++)
          A_rhs[i+1][0][k+1]=A_lhs[0][i][k];
#ifdef _PROFILING
     trans_e2=rtc_();
#endif
      //2.3 update right  data (mmem->lhs)
      get_reply=0;
      athread_get(PE_MODE,&A[src][s_starti][s_endj][s_startk]
                   , &A_lhs[0][0][0]
                   , len,(void*)&get_reply
                   , 0, stride, bsize);  
      while(get_reply!=1);
#ifdef _PROFILING
     trans_s3=rtc_();
#endif
      //2.4 update right data(lhs->rhs)
      for(i = 0; i < BLKX; i++)
         for(k = 0; k < BLKZ; k++)
           A_rhs[i+1][BLKY+1][k+1]=A_lhs[0][i][k];
#ifdef _PROFILING
     trans_e3=rtc_();
#endif
#ifdef _PROFILING
  dma_end1=rtc_();
#endif
   }else{
//no reuse ldm
//1.1  fetch data
   bsize=SIZEZ*(BLKY+2)*8;
   stride=((SIZEY-(BLKY+2))*SIZEZ)*8;  //stride: from blocktail to blockhead 
   tsize= bsize*(BLKX+2);
#ifdef _PROFILING
   dma_start2=rtc_();
#endif
   get_reply=0;   
   athread_get(PE_MODE,&A[src][s_starti-1][s_startj-1][s_startk-1]
               , &A_rhs[0][0][0]
               , tsize, (void*)&get_reply
               , 0, stride, bsize);
   while(get_reply!=1);
#ifdef _PROFILING
  dma_end2=rtc_();
#endif
   last_iter = s_iter;
   last_starti = s_starti;
   last_startj = s_startj;
   last_startk = s_startk;
  }
#ifdef _PROFILING
  start=rtc_();
#endif
 //compute
   for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = 1; kk < BLKZ-BLKZ%4; kk+=4)
         {
            A_lhs[ii-1][jj-1][kk-1] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
            A_lhs[ii-1][jj-1][kk] = alpha_d * (A_rhs[ii][jj][kk+1]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+1] + A_rhs[(ii+1)][jj][kk+1] +\
                     A_rhs[ii][jj-1][kk+1] + A_rhs[ii][(jj+1)][kk+1]+\
                     A_rhs[ii][jj][(kk)] + A_rhs[ii][jj][(kk+2)]);
            A_lhs[ii-1][jj-1][kk+1] = alpha_d * (A_rhs[ii][jj][kk+2]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+2] + A_rhs[(ii+1)][jj][kk+2] +\
                     A_rhs[ii][jj-1][kk+2] + A_rhs[ii][(jj+1)][kk+2]+\
                     A_rhs[ii][jj][(kk+1)] + A_rhs[ii][jj][(kk+3)]);
            A_lhs[ii-1][jj-1][kk+2] = alpha_d * (A_rhs[ii][jj][kk+3]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk+3] + A_rhs[(ii+1)][jj][kk+3] +\
                     A_rhs[ii][jj-1][kk+3] + A_rhs[ii][(jj+1)][kk+3]+\
                     A_rhs[ii][jj][(kk+2)] + A_rhs[ii][jj][(kk+4)]);
         }
     for(ii = 1; ii < BLKX+1; ++ii)
       for(jj = 1; jj < BLKY+1; ++jj)
         for(kk = BLKZ-BLKZ%4; kk < BLKZ+1; kk+=1)
         {
            A_lhs[ii-1][jj-1][kk-1] = alpha_d * (A_rhs[ii][jj][kk]) + \
                     beta_d * (A_rhs[(ii-1)][jj][kk] + A_rhs[(ii+1)][jj][kk] +\
                     A_rhs[ii][jj-1][kk] + A_rhs[ii][(jj+1)][kk]+\
                     A_rhs[ii][jj][(kk-1)] + A_rhs[ii][jj][(kk+1)]);
         }
#ifdef _PROFILING
  end=rtc_();
  slave_comp_count[core_id]+=((end-start));
  dma_start3=rtc_();
#endif
//4 write back
   put_reply=0;
   bsize=BLKZ*8; 
   stride=((SIZEZ-BLKZ))*8;
   tsize= bsize*BLKY; 
   for(i =  s_starti; i < s_endi; ++i)
       athread_put(PE_MODE,&A_lhs[i-s_starti][0][0]
                , &A[dest][i][s_startj][s_startk]
                ,tsize,(void*)&put_reply,stride,bsize);
   while(put_reply!=(BLKX));
#ifdef _PROFILING
  dma_end3=rtc_();
  slave_dma_count[core_id]+=((dma_end1-dma_start1)+(dma_end2-dma_start2)+(dma_end3-dma_start3));
//  slave_dma_hit_count[core_id]+=((dma_end1-dma_start1));
  slave_trans_count[core_id]+=((trans_e1-trans_s1)+(trans_e2-trans_s2)+(trans_e3-trans_s3));
#endif
}
